package la.iok.hzsvn.lewin.movie.service;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import la.iok.hzsvn.lewin.movie.core.RoleEnum;
import la.iok.hzsvn.lewin.movie.core.Token;
import la.iok.hzsvn.lewin.movie.core.exception.ErrorCode;
import la.iok.hzsvn.lewin.movie.core.model.MovieBase;
import la.iok.hzsvn.lewin.movie.core.model.MovieVo;
import la.iok.hzsvn.lewin.movie.core.model.SimpleOrgUnitVo;
import la.iok.hzsvn.lewin.movie.entity.Movie;
import la.iok.hzsvn.lewin.movie.entity.MovieLevel;
import la.iok.hzsvn.lewin.movie.mapper.MovieMapper;
import la.iok.hzsvn.lewin.movie.model.MovieParam;
import la.iok.hzsvn.lewin.movie.services.MovieServiceInvoker;
import la.iok.hzsvn.lewin.mybatis.model.PageVo;
import la.iok.hzsvn.lewin.mybatis.service.BaseServiceImpl;
import la.iok.hzsvn.share.annotations.NotNull;
import la.iok.hzsvn.share.annotations.NotNullElement;
import la.iok.hzsvn.share.annotations.Nullable;
import la.iok.hzsvn.share.collection.IdOperators;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class MovieServiceImpl extends BaseServiceImpl<Movie,MovieMapper,Long> implements MovieService{
    private final MovieLevelService movieLevelService;
    private final MovieServiceInvoker movieServiceInvoker;
    public MovieServiceImpl(MovieMapper movieMapper,
                            MovieLevelService movieLevelService,
                            MovieServiceInvoker movieServiceInvoker) {
        super(movieMapper);
        this.movieLevelService = movieLevelService;
        this.movieServiceInvoker = movieServiceInvoker;
    }

    @Override
    public Long save(@NotNull Token token, @NotNull MovieParam movie) {
        ErrorCode.NAME_BLANK_ERROR.assertNotBlank(movie.getName());
        ErrorCode.INVALID_USER.assertNotNull(token.getUserId());
        ErrorCode.PROPERTY_CHECK_ERROR.assertNotNull(token.getOrgUnitId(),"组织","用户不属于任何组织");
        SimpleOrgUnitVo orgUnit = movieServiceInvoker.orgUnitInfo(token.getOrgUnitId());
        ErrorCode.ORG_UNIT_NOT_EXIST.assertNotNull(orgUnit, token.getOrgUnitId());
        ErrorCode.PROPERTY_CHECK_ERROR.assertTrue(orgUnit.normalStatus(),"状态","组织处于非正常状态");
        Movie entity = new Movie();
        BeanUtils.copyProperties(movie,entity);
        entity.setUserId(token.getUserId());
        entity.setOrgUnitId(token.getOrgUnitId());
        entity.setTag(MovieBase.storeTag(movie.getTag()));
        mapper.insertSelective(entity);
        return entity.getId();
    }

    @Override
    public void update(@Nullable Long id, @NotNull Token token, @NotNull MovieParam movie) {
        if(id == null){
            return;
        }
        Movie entity = validate(token,id);
        BeanUtils.copyProperties(movie,entity);
        entity.setTag(MovieBase.storeTag(movie.getTag()));
        mapper.update(entity);
    }

    @Override
    public void delete(@Nullable Long id, @NotNull Token token) {
        if(id == null){
            return;
        }

        validate(token,id);
        mapper.deleteById(id);
    }

    @NotNull
    @Override
    public MovieVo po2vo(@NotNull Movie movie) {
        MovieLevel level = movieLevelService.select(movie.getMovieLevelId());
        return po2vo(movie,level);
    }

    private MovieVo po2vo(@NotNull Movie movie,@Nullable MovieLevel movieLevel){
        MovieVo vo = new MovieVo();
        BeanUtils.copyProperties(movie,vo);
        if(movieLevel != null){
            vo.setPrice(movieLevel.getPrice());
            vo.setMovieLevelName(movieLevel.getName());
        }
        vo.setTag(showTag(movie.getTag()));
        return vo;
    }

    private String[] showTag(String tag){
        return StringUtils.tokenizeToStringArray(tag,";");
    }

    @Override
    public PageVo<MovieVo> page(int page, int limit, @Nullable String name,@Nullable String author, @Nullable String tag,
                                @Nullable Integer minPlayers, @Nullable Integer maxPlayers,
                                @Nullable Integer minDuration, @Nullable Integer maxDuration) {
        Page<Movie> list = PageHelper.startPage(page,limit);
        mapper.list(name,author,tag,minPlayers,maxPlayers,minDuration,maxDuration);
        PageVo<MovieVo> pageVo = new PageVo<>();
        pageVo.setPages(list.getPages());
        pageVo.setTotal(list.getTotal());
        pageVo.setCurrent(list.getPageNum());
        pageVo.setSize(list.getPageSize());
        pageVo.setRecords(po2vo(list));
        return pageVo;
    }

    @Override
    public Movie validate(@NotNull Token token,@Nullable Long movieId) {
        token.validAnyAuthority(RoleEnum.Admin,RoleEnum.Author);
        Movie movie = select(movieId);
        ErrorCode.MOVIE_NOT_EXIST.assertNotNull(movie,movieId);
        if(!token.hasAuthority(RoleEnum.Admin)){
            ErrorCode.MOVIE_ACCESS_DENIED.assertEqual(movie.getOrgUnitId(),token.getOrgUnitId(),movie.getName());
        }
        return movie;
    }

    @NotNull
    @NotNullElement
    @Override
    public List<Long> unsupportedReturnMoneyMovies() {
        List<MovieLevel> list = movieLevelService.listByProperty("returnMoneySupport", MovieLevel.RETURN_MONEY_UNSUPPORTED);
        Set<Long> unsupportedMovieLevelIds = list.stream().map(MovieLevel::getId).collect(Collectors.toSet());
        List<Movie> unsupportedMovies = listInProperty("movieLevelId",unsupportedMovieLevelIds);
        return unsupportedMovies.stream().map(Movie::getId).collect(Collectors.toList());
    }

    private List<MovieVo> po2vo(Collection<Movie> movies) {
        if(CollectionUtils.isEmpty(movies)){
            return Collections.emptyList();
        }
        Set<Long> set = movies.stream().map(Movie::getMovieLevelId).filter(Objects::nonNull).collect(Collectors.toSet());
        List<MovieLevel> list = movieLevelService.listInProperty("id",set);
        return movies.stream().map(movie -> po2vo(movie, IdOperators.find(list,movie.getMovieLevelId()))).collect(Collectors.toList());
    }
}
